"""File Management tool for the React Agent."""

import os
import shutil
import mimetypes
import hashlib
from pathlib import Path
from typing import Any, Dict, List, Optional, Union
from datetime import datetime
from .base_tool import BaseTool, ToolResult


class FileManagerTool(BaseTool):
    """Tool for safe file management operations."""
    
    # Maximum file size for reading (10MB)
    MAX_FILE_SIZE = 10 * 1024 * 1024
    
    # Maximum number of files to list
    MAX_FILES_LIST = 1000
    
    # Allowed file extensions for reading/writing
    SAFE_TEXT_EXTENSIONS = {
        '.txt', '.md', '.json', '.csv', '.xml', '.yaml', '.yml',
        '.py', '.js', '.html', '.css', '.sql', '.log', '.conf',
        '.ini', '.cfg', '.properties', '.env', '.gitignore',
        '.dockerfile', '.sh', '.bat', '.ps1'
    }
    
    # Blocked directories (security)
    BLOCKED_DIRECTORIES = {
        '/etc', '/bin', '/sbin', '/usr/bin', '/usr/sbin',
        '/System', '/Library', '/Applications',
        'C:\\Windows', 'C:\\Program Files', 'C:\\Program Files (x86)',
        '/root', '/var/log', '/var/run', '/proc', '/sys'
    }
    
    def __init__(self, working_directory: Optional[str] = None, safe_mode: bool = True):
        super().__init__(
            name="file_manager",
            description=self._get_detailed_description()
        )
        self.working_directory = working_directory or os.getcwd()
        self.safe_mode = safe_mode
        
        # Create a safe sandbox directory if needed
        self.sandbox_dir = os.path.join(self.working_directory, "file_sandbox")
        if not os.path.exists(self.sandbox_dir):
            os.makedirs(self.sandbox_dir, exist_ok=True)
    
    def _get_detailed_description(self) -> str:
        """Get detailed description with examples for file operations."""
#         return """Perform safe file management operations including reading, writing, and organizing files.
#
# SUPPORTED OPERATIONS:
#
# • File Reading:
#   - Read text files: read_file(path)
#   - Get file info: file_info(path)
#   - Check file existence: file_exists(path)
#   - Calculate file hash: file_hash(path)
#
# • File Writing:
#   - Create/write files: write_file(path, content)
#   - Append to files: append_file(path, content)
#   - Create empty files: create_file(path)
#
# • Directory Operations:
#   - List directory contents: list_directory(path)
#   - Create directories: create_directory(path)
#   - Get directory size: directory_size(path)
#
# • File Operations:
#   - Copy files: copy_file(source, destination)
#   - Move files: move_file(source, destination)
#   - Delete files: delete_file(path)
#   - Rename files: rename_file(old_path, new_path)
#
# • Search Operations:
#   - Find files by name: find_files(pattern, directory)
#   - Search file content: search_in_files(text, directory)
#   - Filter by extension: filter_by_extension(directory, extension)
#
# SECURITY FEATURES:
# - Safe mode prevents access to system directories
# - File size limits (10MB max for reading)
# - Extension whitelist for text operations
# - Sandbox directory for safe operations
# - Path traversal protection
# - Automatic backup before destructive operations
#
# USAGE EXAMPLES:
# - "read_file('/path/to/file.txt')" → Read file content
# - "write_file('report.txt', 'Hello World')" → Create/write file
# - "list_directory('/home/user/documents')" → List directory contents
# - "find_files('*.py', '/project')" → Find Python files
# - "copy_file('source.txt', 'backup.txt')" → Copy file
# - "file_info('/path/to/file')" → Get file metadata
#
# SUPPORTED FILE TYPES:
# - Text files: .txt, .md, .json, .csv, .xml, .yaml
# - Code files: .py, .js, .html, .css, .sql
# - Config files: .conf, .ini, .cfg, .env
# - Log files: .log
# - Script files: .sh, .bat, .ps1
#
# LIMITATIONS:
# - Text files only (no binary file editing)
# - 10MB file size limit for reading
# - 1000 files maximum in directory listings
# - No access to system directories in safe mode
# - Automatic sandboxing for write operations
#
# COMMON USE CASES:
# - Reading configuration files
# - Creating reports and logs
# - Managing project files
# - File organization and cleanup
# - Content analysis and search
# - Backup and archival operations"""

        return """执行安全的文件管理操作，包括文件的读取、写入和整理。

**支持的操作：**

- **文件读取：**
  - 读取文本文件：read_file(path)
  - 获取文件信息：file_info(path)
  - 检查文件是否存在：file_exists(path)
  - 计算文件哈希值：file_hash(path)

- **文件写入：**
  - 创建/写入文件：write_file(path, content)
  - 向文件追加内容：append_file(path, content)
  - 创建空文件：create_file(path)

- **目录操作：**
  - 列出目录内容：list_directory(path)
  - 创建目录：create_directory(path)
  - 获取目录大小：directory_size(path)

- **文件操作：**
  - 复制文件：copy_file(source, destination)
  - 移动文件：move_file(source, destination)
  - 删除文件：delete_file(path)
  - 重命名文件：rename_file(old_path, new_path)

- **搜索操作：**
  - 按名称查找文件：find_files(pattern, directory)
  - 搜索文件内容：search_in_files(text, directory)
  - 按扩展名筛选：filter_by_extension(directory, extension)

**安全特性：**
- 安全模式阻止访问系统目录
- 文件大小限制（读取最大为10MB）
- 文本操作的扩展名白名单
- 用于安全操作的沙箱目录
- 路径遍历保护
- 破坏性操作前自动备份

**使用示例：**
- "read_file('/path/to/file.txt')" → 读取文件内容
- "write_file('report.txt', 'Hello World')" → 创建/写入文件
- "list_directory('/home/user/documents')" → 列出目录内容
- "find_files('*.py', '/project')" → 查找Python文件
- "copy_file('source.txt', 'backup.txt')" → 复制文件
- "file_info('/path/to/file')" → 获取文件元数据

**支持的文件类型：**
- 文本文件：.txt、.md、.json、.csv、.xml、.yaml
- 代码文件：.py、.js、.html、.css、.sql
- 配置文件：.conf、.ini、.cfg、.env
- 日志文件：.log
- 脚本文件：.sh、.bat、.ps1

**限制：**
- 仅支持文本文件（不支持二进制文件编辑）
- 读取文件大小限制为10MB
- 目录列表中最多显示1000个文件
- 安全模式下无法访问系统目录
- 写入操作自动进行沙箱隔离

**常见使用场景：**
- 读取配置文件
- 创建报告和日志
- 管理项目文件
- 文件整理和清理
- 内容分析和搜索
- 备份和归档操作"""

    async def execute(self, query: str, **kwargs) -> ToolResult:
        """Execute file management operation."""
        try:
            # Parse the operation from the query
            operation = self._parse_operation(query)
            
            if not operation:
                return ToolResult(
                    success=False,
                    data=None,
                    error="Could not parse file operation from query. Use format: operation(parameters)"
                )
            
            # Execute the operation
            result = await self._execute_operation(operation)
            
            return result
        
        except Exception as e:
            return ToolResult(
                success=False,
                data=None,
                error=f"File operation failed: {str(e)}"
            )
    
    def _parse_operation(self, query: str) -> Optional[Dict[str, Any]]:
        """Parse operation from query string."""
        query = query.strip()
        
        # Simple operation parsing
        operations = {
            'read_file': ['read_file', 'read', 'cat', 'show'],
            'write_file': ['write_file', 'write', 'create'],
            'append_file': ['append_file', 'append'],
            'list_directory': ['list_directory', 'list', 'ls', 'dir'],
            'file_info': ['file_info', 'info', 'stat'],
            'file_exists': ['file_exists', 'exists'],
            'copy_file': ['copy_file', 'copy', 'cp'],
            'move_file': ['move_file', 'move', 'mv'],
            'delete_file': ['delete_file', 'delete', 'rm'],
            'create_directory': ['create_directory', 'mkdir'],
            'find_files': ['find_files', 'find'],
            'search_in_files': ['search_in_files', 'search', 'grep'],
            'file_hash': ['file_hash', 'hash', 'checksum']
        }
        
        # Try to match operation
        for op_name, keywords in operations.items():
            for keyword in keywords:
                if query.lower().startswith(keyword.lower()):
                    # Extract parameters
                    params = self._extract_parameters(query, keyword)
                    return {
                        'operation': op_name,
                        'parameters': params
                    }
        
        return None
    
    def _extract_parameters(self, query: str, keyword: str) -> List[str]:
        """Extract parameters from query."""
        # Remove the keyword and clean up
        remaining = query[len(keyword):].strip()
        
        # Handle different parameter formats
        if remaining.startswith('(') and remaining.endswith(')'):
            # Function call format: operation(param1, param2)
            params_str = remaining[1:-1]
            params = [p.strip().strip('"\'') for p in params_str.split(',')]
        else:
            # Space-separated format: operation param1 param2
            params = [p.strip().strip('"\'') for p in remaining.split()]
        
        return [p for p in params if p]  # Remove empty parameters
    
    async def _execute_operation(self, operation: Dict[str, Any]) -> ToolResult:
        """Execute the parsed operation."""
        op_name = operation['operation']
        params = operation['parameters']
        
        try:
            if op_name == 'read_file':
                return await self._read_file(params[0] if params else '')
            elif op_name == 'write_file':
                return await self._write_file(
                    params[0] if len(params) > 0 else '',
                    params[1] if len(params) > 1 else ''
                )
            elif op_name == 'append_file':
                return await self._append_file(
                    params[0] if len(params) > 0 else '',
                    params[1] if len(params) > 1 else ''
                )
            elif op_name == 'list_directory':
                return await self._list_directory(params[0] if params else '.')
            elif op_name == 'file_info':
                return await self._file_info(params[0] if params else '')
            elif op_name == 'file_exists':
                return await self._file_exists(params[0] if params else '')
            elif op_name == 'copy_file':
                return await self._copy_file(
                    params[0] if len(params) > 0 else '',
                    params[1] if len(params) > 1 else ''
                )
            elif op_name == 'move_file':
                return await self._move_file(
                    params[0] if len(params) > 0 else '',
                    params[1] if len(params) > 1 else ''
                )
            elif op_name == 'delete_file':
                return await self._delete_file(params[0] if params else '')
            elif op_name == 'create_directory':
                return await self._create_directory(params[0] if params else '')
            elif op_name == 'find_files':
                return await self._find_files(
                    params[0] if len(params) > 0 else '*',
                    params[1] if len(params) > 1 else '.'
                )
            elif op_name == 'search_in_files':
                return await self._search_in_files(
                    params[0] if len(params) > 0 else '',
                    params[1] if len(params) > 1 else '.'
                )
            elif op_name == 'file_hash':
                return await self._file_hash(params[0] if params else '')
            else:
                return ToolResult(
                    success=False,
                    data=None,
                    error=f"Unknown operation: {op_name}"
                )
        
        except IndexError:
            return ToolResult(
                success=False,
                data=None,
                error=f"Insufficient parameters for operation: {op_name}"
            )
    
    def _is_safe_path(self, path: str) -> bool:
        """Check if path is safe to access."""
        if not self.safe_mode:
            return True
        
        try:
            # Handle relative paths by resolving them relative to working directory
            if not os.path.isabs(path):
                path = os.path.join(self.working_directory, path)
            
            # Resolve the absolute path
            abs_path = os.path.abspath(path)
            
            # Check against blocked directories
            for blocked in self.BLOCKED_DIRECTORIES:
                if abs_path.startswith(blocked):
                    return False
            
            # Ensure path is within working directory or sandbox
            working_abs = os.path.abspath(self.working_directory)
            sandbox_abs = os.path.abspath(self.sandbox_dir)
            
            return (abs_path.startswith(working_abs) or 
                   abs_path.startswith(sandbox_abs) or
                   abs_path.startswith('/tmp/') or  # Allow temp directories
                   abs_path.startswith('/var/folders/'))  # Allow macOS temp directories
        
        except Exception:
            return False
    
    def _is_safe_extension(self, path: str) -> bool:
        """Check if file extension is safe for text operations."""
        ext = Path(path).suffix.lower()
        return ext in self.SAFE_TEXT_EXTENSIONS or ext == ''
    
    async def _read_file(self, path: str) -> ToolResult:
        """Read file content."""
        if not path:
            return ToolResult(success=False, data=None, error="File path required")
        
        # Handle relative paths by resolving them relative to working directory
        if not os.path.isabs(path):
            path = os.path.join(self.working_directory, path)
        
        if not self._is_safe_path(path):
            return ToolResult(success=False, data=None, error="Access denied: unsafe path")
        
        try:
            if not os.path.exists(path):
                return ToolResult(success=False, data=None, error="File not found")
            
            if not os.path.isfile(path):
                return ToolResult(success=False, data=None, error="Path is not a file")
            
            # Check file size
            file_size = os.path.getsize(path)
            if file_size > self.MAX_FILE_SIZE:
                return ToolResult(
                    success=False, 
                    data=None, 
                    error=f"File too large: {file_size} bytes (max: {self.MAX_FILE_SIZE})"
                )
            
            # Read file content
            with open(path, 'r', encoding='utf-8', errors='replace') as f:
                content = f.read()
            
            return ToolResult(
                success=True,
                data={
                    "path": path,
                    "content": content,
                    "size": file_size,
                    "lines": len(content.splitlines())
                },
                metadata={"operation": "read_file"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"Read failed: {str(e)}")
    
    async def _write_file(self, path: str, content: str) -> ToolResult:
        """Write content to file."""
        if not path:
            return ToolResult(success=False, data=None, error="File path required")
        
        if not content:
            content = ""
        
        # Handle relative paths by resolving them relative to working directory
        if not os.path.isabs(path):
            path = os.path.join(self.working_directory, path)
        
        # Use sandbox for new files if in safe mode and path is not already safe
        if self.safe_mode and not self._is_safe_path(path):
            path = os.path.join(self.sandbox_dir, os.path.basename(path))
        
        if not self._is_safe_path(path):
            return ToolResult(success=False, data=None, error="Access denied: unsafe path")
        
        if not self._is_safe_extension(path):
            return ToolResult(success=False, data=None, error="Unsafe file extension")
        
        try:
            # Create directory if it doesn't exist
            os.makedirs(os.path.dirname(path), exist_ok=True)
            
            # Backup existing file
            backup_path = None
            if os.path.exists(path):
                backup_path = f"{path}.backup_{int(datetime.now().timestamp())}"
                shutil.copy2(path, backup_path)
            
            # Write file
            with open(path, 'w', encoding='utf-8') as f:
                f.write(content)
            
            file_size = os.path.getsize(path)
            
            return ToolResult(
                success=True,
                data={
                    "path": path,
                    "size": file_size,
                    "lines": len(content.splitlines()),
                    "backup_path": backup_path
                },
                metadata={"operation": "write_file"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"Write failed: {str(e)}")
    
    async def _append_file(self, path: str, content: str) -> ToolResult:
        """Append content to file."""
        if not path:
            return ToolResult(success=False, data=None, error="File path required")
        
        # Handle relative paths by resolving them relative to working directory
        if not os.path.isabs(path):
            path = os.path.join(self.working_directory, path)
        
        if not self._is_safe_path(path):
            return ToolResult(success=False, data=None, error="Access denied: unsafe path")
        
        try:
            # Create file if it doesn't exist
            if not os.path.exists(path):
                return await self._write_file(path, content)
            
            with open(path, 'a', encoding='utf-8') as f:
                f.write(content)
            
            file_size = os.path.getsize(path)
            
            return ToolResult(
                success=True,
                data={
                    "path": path,
                    "size": file_size,
                    "appended_content": content
                },
                metadata={"operation": "append_file"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"Append failed: {str(e)}")
    
    async def _list_directory(self, path: str) -> ToolResult:
        """List directory contents."""
        if not path:
            path = self.working_directory
        
        if not self._is_safe_path(path):
            return ToolResult(success=False, data=None, error="Access denied: unsafe path")
        
        try:
            if not os.path.exists(path):
                return ToolResult(success=False, data=None, error="Directory not found")
            
            if not os.path.isdir(path):
                return ToolResult(success=False, data=None, error="Path is not a directory")
            
            items = []
            count = 0
            
            for item in os.listdir(path):
                if count >= self.MAX_FILES_LIST:
                    break
                
                item_path = os.path.join(path, item)
                try:
                    stat = os.stat(item_path)
                    items.append({
                        "name": item,
                        "type": "directory" if os.path.isdir(item_path) else "file",
                        "size": stat.st_size,
                        "modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
                        "permissions": oct(stat.st_mode)[-3:]
                    })
                    count += 1
                except OSError:
                    continue
            
            return ToolResult(
                success=True,
                data={
                    "path": path,
                    "items": items,
                    "total_items": len(items),
                    "truncated": count >= self.MAX_FILES_LIST
                },
                metadata={"operation": "list_directory"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"List failed: {str(e)}")
    
    async def _file_info(self, path: str) -> ToolResult:
        """Get file information."""
        if not path:
            return ToolResult(success=False, data=None, error="File path required")
        
        if not self._is_safe_path(path):
            return ToolResult(success=False, data=None, error="Access denied: unsafe path")
        
        try:
            if not os.path.exists(path):
                return ToolResult(success=False, data=None, error="File not found")
            
            stat = os.stat(path)
            mime_type, _ = mimetypes.guess_type(path)
            
            info = {
                "path": path,
                "name": os.path.basename(path),
                "type": "directory" if os.path.isdir(path) else "file",
                "size": stat.st_size,
                "created": datetime.fromtimestamp(stat.st_ctime).isoformat(),
                "modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
                "accessed": datetime.fromtimestamp(stat.st_atime).isoformat(),
                "permissions": oct(stat.st_mode)[-3:],
                "mime_type": mime_type,
                "extension": Path(path).suffix.lower()
            }
            
            return ToolResult(
                success=True,
                data=info,
                metadata={"operation": "file_info"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"Info failed: {str(e)}")
    
    async def _file_exists(self, path: str) -> ToolResult:
        """Check if file exists."""
        if not path:
            return ToolResult(success=False, data=None, error="File path required")
        
        try:
            exists = os.path.exists(path)
            return ToolResult(
                success=True,
                data={
                    "path": path,
                    "exists": exists,
                    "type": "directory" if os.path.isdir(path) else "file" if exists else None
                },
                metadata={"operation": "file_exists"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"Check failed: {str(e)}")
    
    async def _copy_file(self, source: str, destination: str) -> ToolResult:
        """Copy file."""
        if not source or not destination:
            return ToolResult(success=False, data=None, error="Source and destination paths required")
        
        if not self._is_safe_path(source) or not self._is_safe_path(destination):
            return ToolResult(success=False, data=None, error="Access denied: unsafe path")
        
        try:
            if not os.path.exists(source):
                return ToolResult(success=False, data=None, error="Source file not found")
            
            if os.path.isdir(source):
                shutil.copytree(source, destination)
            else:
                shutil.copy2(source, destination)
            
            return ToolResult(
                success=True,
                data={
                    "source": source,
                    "destination": destination,
                    "size": os.path.getsize(destination)
                },
                metadata={"operation": "copy_file"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"Copy failed: {str(e)}")
    
    async def _move_file(self, source: str, destination: str) -> ToolResult:
        """Move file."""
        if not source or not destination:
            return ToolResult(success=False, data=None, error="Source and destination paths required")
        
        if not self._is_safe_path(source) or not self._is_safe_path(destination):
            return ToolResult(success=False, data=None, error="Access denied: unsafe path")
        
        try:
            if not os.path.exists(source):
                return ToolResult(success=False, data=None, error="Source file not found")
            
            shutil.move(source, destination)
            
            return ToolResult(
                success=True,
                data={
                    "source": source,
                    "destination": destination
                },
                metadata={"operation": "move_file"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"Move failed: {str(e)}")
    
    async def _delete_file(self, path: str) -> ToolResult:
        """Delete file."""
        if not path:
            return ToolResult(success=False, data=None, error="File path required")
        
        if not self._is_safe_path(path):
            return ToolResult(success=False, data=None, error="Access denied: unsafe path")
        
        try:
            if not os.path.exists(path):
                return ToolResult(success=False, data=None, error="File not found")
            
            # Create backup before deletion
            backup_path = f"{path}.deleted_{int(datetime.now().timestamp())}"
            if os.path.isdir(path):
                shutil.copytree(path, backup_path)
                shutil.rmtree(path)
            else:
                shutil.copy2(path, backup_path)
                os.remove(path)
            
            return ToolResult(
                success=True,
                data={
                    "path": path,
                    "backup_path": backup_path
                },
                metadata={"operation": "delete_file"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"Delete failed: {str(e)}")
    
    async def _create_directory(self, path: str) -> ToolResult:
        """Create directory."""
        if not path:
            return ToolResult(success=False, data=None, error="Directory path required")
        
        if not self._is_safe_path(path):
            return ToolResult(success=False, data=None, error="Access denied: unsafe path")
        
        try:
            os.makedirs(path, exist_ok=True)
            
            return ToolResult(
                success=True,
                data={"path": path},
                metadata={"operation": "create_directory"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"Create directory failed: {str(e)}")
    
    async def _find_files(self, pattern: str, directory: str) -> ToolResult:
        """Find files by pattern."""
        if not pattern:
            pattern = "*"
        if not directory:
            directory = self.working_directory
        
        if not self._is_safe_path(directory):
            return ToolResult(success=False, data=None, error="Access denied: unsafe path")
        
        try:
            import fnmatch
            
            matches = []
            count = 0
            
            for root, dirs, files in os.walk(directory):
                if count >= self.MAX_FILES_LIST:
                    break
                
                for file in files:
                    if fnmatch.fnmatch(file, pattern):
                        file_path = os.path.join(root, file)
                        matches.append({
                            "path": file_path,
                            "name": file,
                            "directory": root,
                            "size": os.path.getsize(file_path)
                        })
                        count += 1
                        
                        if count >= self.MAX_FILES_LIST:
                            break
            
            return ToolResult(
                success=True,
                data={
                    "pattern": pattern,
                    "directory": directory,
                    "matches": matches,
                    "total_matches": len(matches),
                    "truncated": count >= self.MAX_FILES_LIST
                },
                metadata={"operation": "find_files"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"Find failed: {str(e)}")
    
    async def _search_in_files(self, text: str, directory: str) -> ToolResult:
        """Search for text in files."""
        if not text:
            return ToolResult(success=False, data=None, error="Search text required")
        if not directory:
            directory = self.working_directory
        
        if not self._is_safe_path(directory):
            return ToolResult(success=False, data=None, error="Access denied: unsafe path")
        
        try:
            matches = []
            count = 0
            
            for root, dirs, files in os.walk(directory):
                if count >= self.MAX_FILES_LIST:
                    break
                
                for file in files:
                    file_path = os.path.join(root, file)
                    
                    # Only search in text files
                    if not self._is_safe_extension(file_path):
                        continue
                    
                    try:
                        # Check file size
                        if os.path.getsize(file_path) > self.MAX_FILE_SIZE:
                            continue
                        
                        with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                            content = f.read()
                            if text.lower() in content.lower():
                                # Find line numbers
                                lines = content.splitlines()
                                matching_lines = []
                                for i, line in enumerate(lines, 1):
                                    if text.lower() in line.lower():
                                        matching_lines.append({
                                            "line_number": i,
                                            "content": line.strip()
                                        })
                                
                                matches.append({
                                    "path": file_path,
                                    "name": file,
                                    "matching_lines": matching_lines[:10]  # Limit to 10 lines
                                })
                                count += 1
                                
                                if count >= self.MAX_FILES_LIST:
                                    break
                    
                    except (UnicodeDecodeError, PermissionError):
                        continue
            
            return ToolResult(
                success=True,
                data={
                    "search_text": text,
                    "directory": directory,
                    "matches": matches,
                    "total_matches": len(matches),
                    "truncated": count >= self.MAX_FILES_LIST
                },
                metadata={"operation": "search_in_files"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"Search failed: {str(e)}")
    
    async def _file_hash(self, path: str) -> ToolResult:
        """Calculate file hash."""
        if not path:
            return ToolResult(success=False, data=None, error="File path required")
        
        if not self._is_safe_path(path):
            return ToolResult(success=False, data=None, error="Access denied: unsafe path")
        
        try:
            if not os.path.exists(path):
                return ToolResult(success=False, data=None, error="File not found")
            
            if not os.path.isfile(path):
                return ToolResult(success=False, data=None, error="Path is not a file")
            
            # Calculate multiple hashes
            md5_hash = hashlib.md5()
            sha256_hash = hashlib.sha256()
            
            with open(path, 'rb') as f:
                for chunk in iter(lambda: f.read(4096), b""):
                    md5_hash.update(chunk)
                    sha256_hash.update(chunk)
            
            return ToolResult(
                success=True,
                data={
                    "path": path,
                    "md5": md5_hash.hexdigest(),
                    "sha256": sha256_hash.hexdigest(),
                    "size": os.path.getsize(path)
                },
                metadata={"operation": "file_hash"}
            )
        
        except Exception as e:
            return ToolResult(success=False, data=None, error=f"Hash calculation failed: {str(e)}")
    
    def get_schema(self) -> Dict[str, Any]:
        """Get the tool's input schema."""
        return {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    # "description": "File operation to perform. Format: operation(parameters). Supports read_file, write_file, list_directory, file_info, copy_file, move_file, delete_file, find_files, search_in_files, and more."
                    "description": "要执行的文件操作。格式：操作(参数)。支持读取文件、写入文件、列出目录、文件信息、复制文件、移动文件、删除文件、查找文件、在文件中搜索等。"
                }
            },
            "required": ["query"],
            "examples": [
                "read_file('config.txt')",
                "write_file('report.txt', 'Hello World')",
                "list_directory('/home/user/documents')",
                "file_info('/path/to/file.txt')",
                "copy_file('source.txt', 'backup.txt')",
                "find_files('*.py', '/project')",
                "search_in_files('TODO', '/project')",
                "file_hash('important.txt')",
                "create_directory('new_folder')",
                "file_exists('config.json')"
            ]
        }
    
    def set_working_directory(self, directory: str) -> bool:
        """Set the working directory."""
        try:
            if os.path.isdir(directory):
                self.working_directory = os.path.abspath(directory)
                return True
            return False
        except Exception:
            return False
    
    def get_working_directory(self) -> str:
        """Get the current working directory."""
        return self.working_directory
    
    def get_sandbox_directory(self) -> str:
        """Get the sandbox directory."""
        return self.sandbox_dir
    
    def toggle_safe_mode(self, enabled: bool) -> None:
        """Toggle safe mode on/off."""
        self.safe_mode = enabled